#include "app_mqtt_client.h"

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_system.h"
#include "esp_partition.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
//#include "protocol_examples_common.h"
#include "esp_system.h"

#include "cJSON.h"

#include "esp_log.h"
#include "mqtt_client.h"
#include "esp_tls.h"
#include "esp_ota_ops.h"
#include <sys/param.h>

#include "esp_audio.h"
#include "playlist.h"
#include "sdcard_list.h"

#include "app_music_play.h"
#include "app_console_debug.h"
#include "app_nvs_param.h"


static const char *TAG = "MQTTS_CLIENT";

esp_mqtt_client_handle_t client;

mqtt_nvs_param param_mqtt;

#define TOPIC_BUF_SIZE 50
char topic_buf[TOPIC_BUF_SIZE] = {0};

static bool is_connected = false;

extern esp_audio_handle_t  player;
extern playlist_operator_handle_t  playlist;

extern const uint8_t mqtt_eclipseprojects_io_pem_start[]   asm("_binary_mqtt_eclipseprojects_io_pem_start");

extern const uint8_t mqtt_eclipseprojects_io_pem_end[]   asm("_binary_mqtt_eclipseprojects_io_pem_end");


static char* mqtt_cmd_packet(char* str_type, int value);

static void mqtt_event_connected_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    esp_mqtt_event_handle_t event = event_data;
//    esp_mqtt_client_handle_t client = event->client;
    int msg_id;

    ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
    memset(topic_buf, 0, TOPIC_BUF_SIZE);
    sprintf(topic_buf, "/topic/command/%s", param_mqtt.username);
    msg_id = esp_mqtt_client_subscribe(client, topic_buf, 0);
    memset(topic_buf, 0, TOPIC_BUF_SIZE);
    sprintf(topic_buf, "/topic/state/%s", param_mqtt.username);
    msg_id = esp_mqtt_client_publish(client, topic_buf, "online", 0, 0, 0);
    ESP_LOGI(TAG, "TOPIC:%s\n content:online\n", topic_buf);

    is_connected = true;
    ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
}

static void mqtt_event_disconnected_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    esp_mqtt_event_handle_t event = event_data;
//    esp_mqtt_client_handle_t client = event->client;
    int msg_id;
    is_connected = true;
    ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
}

static void mqtt_event_subscribed_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    esp_mqtt_event_handle_t event = event_data;
//    esp_mqtt_client_handle_t client = event->client;
    int msg_id;

    ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
    // msg_id = esp_mqtt_client_publish(client, "/topic/qos0", mqtt_cmd_packet("set_vol", 60), 0, 0, 0);
    // ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
}

static void mqtt_event_unsubscribed_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    esp_mqtt_event_handle_t event = event_data;
//    esp_mqtt_client_handle_t client = event->client;
    int msg_id;

    ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
}

static void mqtt_event_published_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    esp_mqtt_event_handle_t event = event_data;
//    esp_mqtt_client_handle_t client = event->client;
    int msg_id;

    ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
}


static char* mqtt_cmd_packet(char* str_type, int value)
{
    cJSON* cjson = NULL;
    char* data;

    cjson = cJSON_CreateObject();
    if(cjson == NULL)
        return NULL;
    cJSON_AddStringToObject(cjson, "type", str_type);
    cJSON_AddNumberToObject(cjson, "value", value);

    data =  cJSON_Print(cjson);

    cJSON_Delete(cjson);

    return data;
}

static int app_audio_get_list(char** data)
{
#define LIST_GET_NUM_MAX 5
#define LIST_SONG_URL_SIZE_MAX 256
#define LIST_JSON_BUFF_SIZE_MAX 500
    cJSON* root = NULL;
    cJSON* song_list = NULL;

    uint8_t i = 0;

    char* url;
    char* json_print;

    root = cJSON_CreateObject();
    song_list = cJSON_CreateArray();

    while (i < LIST_GET_NUM_MAX)
    {
        /* code */
        if(sdcard_list_choose(playlist, i++, &url) == ESP_FAIL)
            break;

        cJSON_AddItemToArray(song_list, cJSON_CreateString(url));
    }

    cJSON_AddItemToObject(root, "list", song_list);
    cJSON_AddNumberToObject(root, "num", i);

    json_print = cJSON_Print(root);


    //ESP_LOGI(TAG, "song list json:%s", json_print);

    cJSON_Delete(root);
    *data = json_print;

    return ESP_OK;
}

int mqtt_publish_music_list_pack(void)
{
    int msg_id;
    void* json_buf = NULL;
    app_audio_get_list(&json_buf);
    //msg_id = esp_mqtt_client_publish(client, "/topic/info_list", json_buf, 0, 0, 0);
    memset(topic_buf, 0, TOPIC_BUF_SIZE);
    sprintf(topic_buf, "/topic/list/%s", param_mqtt.username);
    msg_id = esp_mqtt_client_publish(client, topic_buf, json_buf, 0, 0, 0);
    ESP_LOGI(TAG, "TOPIC:%s\n content:%s\n", topic_buf, (char*)json_buf);
    free(json_buf);
    return 0;
}


int mqtt_publish_state_pack(void)
{
    int msg_id;
    esp_audio_state_t state;
    cJSON* root = NULL;
    char* json_print;
    char* url;

    if(is_connected == false){
        ESP_LOGI(TAG, "mqtt publish state failed,reason:mqtt disconnect.");
        return ESP_FAIL;
    }

    root = cJSON_CreateObject();
    
    if(esp_audio_state_get(player, &state) != ESP_OK)
    {
        ESP_LOGE(TAG, "mqtt publish state failed,reason:get state failed.");
        return ESP_FAIL;
    }

    if(sdcard_list_current(playlist, &url) != ESP_OK)
    {
        ESP_LOGE(TAG, "mqtt publish state failed,reason:get URL failed.");
        return ESP_FAIL;       
    }

    cJSON_AddNumberToObject(root, "status", state.status);
    cJSON_AddNumberToObject(root, "err_msg", state.err_msg);
    cJSON_AddNumberToObject(root, "media_src", state.media_src);
    cJSON_AddStringToObject(root, "url", url);

    json_print = cJSON_Print(root);
    ESP_LOGI(TAG, "music state json:%s", json_print);
//    msg_id = esp_mqtt_client_publish(client, "/topic/state", json_print, 0, 0, 0);
    memset(topic_buf, 0, TOPIC_BUF_SIZE);
    sprintf(topic_buf, "/topic/state/%s", param_mqtt.username);
    msg_id = esp_mqtt_client_publish(client, topic_buf, json_print, 0, 0, 0);
    free(json_print);

    return ESP_OK;
}

static int mqtt_cmd_parse(void *data)
{
    cJSON* cjson = NULL;
    cJSON* cjson_type = NULL;
    cJSON* cjson_value = NULL;

    char* str_type;
    int value = 0;

    cjson = cJSON_Parse((const char*)data);
    if(cjson == NULL){
        ESP_LOGI(TAG, "cjson parse failed.");
        return 1;
    }

    cjson_type = cJSON_GetObjectItem(cjson, "type");
    if(cjson_type == NULL){
        ESP_LOGI(TAG, "cjson_type parse failed.");
        return 1;
    }

    cjson_value = cJSON_GetObjectItem(cjson, "value");
    if(cjson_value == NULL){
        ESP_LOGI(TAG, "cjson_value parse failed.");
        return 1;
    }

    str_type = cJSON_GetStringValue(cjson_type);
    // value = cJSON_GetNumberValue(cjson_value);

    ESP_LOGI(TAG, "----mqtt json parse----");
    ESP_LOGI(TAG, "cmd type: %s", str_type);

    if(strcmp(str_type, "play") == 0)
    {
        app_audio_stop();
        if(cJSON_IsString(cjson_value)){
            char* url = cJSON_GetStringValue(cjson_value);
            if(url != NULL)
                app_audio_play(url,  0);
        }else
        {
            app_audio_play(NULL,  0);
        }
    }
    else
    if(strcmp(str_type, "stop") == 0)
    {
        app_audio_stop();
    }
    if(strcmp(str_type, "next") == 0)
    {
        app_audio_stop();
        app_audio_playlist_sd_next(1);
    }
    else
    if(strcmp(str_type, "set_vol") == 0)
    {
        value = cJSON_GetNumberValue(cjson_value);
        app_audio_set_vol(value);
    }
    else
    if(strcmp(str_type, "get_list") == 0)
    {
        mqtt_publish_music_list_pack();
    }
    else
        ESP_LOGI(TAG, "cmd not support.");

   ESP_LOGI(TAG, "------------------------");

    if(cjson != NULL)
        cJSON_Delete(cjson);

    // if(str_type != NULL)
    //     free(str_type);

    return 0;

}

static void mqtt_event_data_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    esp_mqtt_event_handle_t event = event_data;
//    esp_mqtt_client_handle_t client = event->client;
    int msg_id = 0;

    ESP_LOGI(TAG, "MQTT_EVENT_DATA");
    printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
    printf("DATA=%.*s\r\n", event->data_len, event->data);
    mqtt_cmd_parse(event->data);
}

bool mqtt_get_state(void)
{
    return is_connected;    
}

void initialise_mqtt_client(void)
{
//   mqtt_nvs_param param_mqtt;
    app_get_mqtt_param(&param_mqtt);

    esp_mqtt_client_config_t mqtt_cfg= {0};

    mqtt_cfg.credentials.username = param_mqtt.username;
    mqtt_cfg.credentials.authentication.password = param_mqtt.password;
    mqtt_cfg.broker.address.uri = param_mqtt.url;
    mqtt_cfg.broker.verification.certificate = (const char *)mqtt_eclipseprojects_io_pem_start;

    ESP_LOGI(TAG, "[APP] Free memory: %d bytes", (int)esp_get_free_heap_size());
    client = esp_mqtt_client_init(&mqtt_cfg);
    /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
//    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);

    esp_mqtt_client_register_event(client, MQTT_EVENT_CONNECTED, mqtt_event_connected_handler, NULL);
    esp_mqtt_client_register_event(client, MQTT_EVENT_DISCONNECTED, mqtt_event_disconnected_handler, NULL);
    esp_mqtt_client_register_event(client, MQTT_EVENT_SUBSCRIBED, mqtt_event_subscribed_handler, NULL);
    esp_mqtt_client_register_event(client, MQTT_EVENT_UNSUBSCRIBED, mqtt_event_unsubscribed_handler, NULL);
    esp_mqtt_client_register_event(client, MQTT_EVENT_PUBLISHED, mqtt_event_published_handler, NULL);
    esp_mqtt_client_register_event(client, MQTT_EVENT_DATA, mqtt_event_data_handler, NULL);

    esp_mqtt_client_start(client);
}

